上一篇博客介绍到Gradle实践之自定义打包jar+Log开关自动关闭。可以自己定义打包的jar已经不错了,但是还是不够爽,怎么办?自己写一个Plugin!会用轮子,也要会造轮子是不是,我们经常使用到的com.android.library
和com.android.application
都是Google给我们提供的Gradle插件,里面已经实现了大部分App开发者所需要的功能。Github上面也已经有很多gradle插件,但是如果我们是程序猿,我们总是可以有需求是人家的Plugin无法满足的,那好吧,我们自己写个插件。
准备工作
开发环境(以Win7为例)
IDE开发环境:我这篇例子就用Android Stuido来写的,使用AS写会有一点点的奇怪,因为AS默认新建的都是Android工程,可是!用它来写一个Gradle Plugin并没有任何问题!但实际上Gradle不仅仅可以给Android项目使用。所以我推荐大家去体验一下JetBrains家的Java IDE——Intelligent Java IDE。我们用的Android Studio就是基于这个开发的。
JDK:我这次用的是Windows系统,安装了1.8和1.7的JDK,等下写Gradle插件的时候会指定一个版本的JDK。至于指定哪个版本的JDK,会遇到什么问题,我会在后面提到。不过不管你使用什么版本的JDK,必须用交叉编译选项来编译我们的插件,以保证别人能在低版本的JDK上运行我们的插件。交叉编译选项我会在build.gradle文件中会特别标注一下。 注意 ,JDK必须要安装,Groovy最后也要compile成Jar包。
Gradle:一般我们用Android Studio开发的时候都已经配置好了这个。我是让它翻墙自己下载的,下载后的目录都在C盘的Users目录下。例如我的
GRADLE_HOME
是C:\Users\noughtchen\.gradle\wrapper\dists\gradle-2.4-all\6r4uqcc6ovnq6ac6s0txzcpc0\gradle-2.4
。为了确保你安装了Gradle并配置了环境变量,可以在命令行输入一句:
|
|
如果终端上显示了:
|
|
那就说明gradle是OK的。
- Groovy SDK(可跳过):这个类似于JDK,因为Gradle插件使用Groovy语言编写,所以我们也可以安装Groovy SDK。这里是官方的安装教程,非常简单。分为三步:
- 下载一个Binary Release版的zip包,解压到你本地的一个目录下,例如我本地是
D:\mydev\groovy-2.4.5
。 - 添加名为
GROOVY_HOME
的环境变量,它的值为刚才的目录D:\mydev\groovy-2.4.5
。 - 然后将
GROOVY_HOME/bin
添加到系统的环境变量Path
里,添加的值为%GROOVY_HOME%\bin
。
- 下载一个Binary Release版的zip包,解压到你本地的一个目录下,例如我本地是
Groovy基础
这里可以忽略,只要懂Java和一点基本Groovy语法就行,实际上我只是在打包我们SDK项目的时候自学了一点,另外参考了一下Google官方的Gradle插件就够了,实在不会的可以再去查看Gradle官方的Document。下面是几个可以学习的资源:
- http://tools.android.com/build/gradleplugin 这里介绍了怎么从Google Checkout下来官方的Gradle插件源码,以及Android Studio的源码!开源万岁,真是碉堡,推荐大家看一下这个,这样就知道APK是怎么打包的了。
- https://docs.gradle.org/current/userguide/custom_plugins.html Gradle官方的自定义插件文档,这篇是必看的,非常简单的HelloWorld例子。看完肯定还是不会写Gradle插件的,呵呵!
自定义Gradle Plugin
不废话了,下面进入正式的开发过程,这次我们在前面Gradle实践之自定义打包jar+Log开关自动关闭的基础上,把打包Jar、混淆Jar包的任务都丢到插件里,然后呢,我们这次自己写JavaCompile任务,这样一来,如果是做SDK开发的同学,实际上几乎就可以不用com.android.library
插件了,当然我个人觉得吧,能不重复造轮子就不要重复造了,等下会大家看到这点的。
1.新建一个Groovy工程
如下图所示,我新建了一个名为HelloGradlePlugin
的文件夹。
接着在文件夹里新建了一个名为build.gradle
的文件,里面现在没有任何代码。
这里有点奇怪吗?是的,为啥不是用AS直接new一个project?前面说了,AS默认新建的只能选择Android Project,下面我们看看怎么使用这一步建立的文件夹。
2.建立项目结构
打开Android Studio,选择File->Open
,打开刚才这个文件夹。如图所示:
这时AS会提示我们是否使用本地的Gradle Wrapper
,点击yes
,AS会帮我们在HelloGradlePlugin
文件夹下面自动生成对应的gradle文件夹和文件。
这样,我们就算在AS中导入我们的Gradle Plugin工程了。下面我们建立项目结构。
首先,在src
文件夹下面分别建立main/groovy/
文件夹和resources/META-INF/gradle-plugins
。
然后,在src/main/resources/META-INF/gradle-plugins
文件夹下面新建一个hello-world-plugin.properties
文件,这个文件的名字就是我们就是我们这个Gradle插件的名字(即name
),将来在其他项目中引用这个插件的时候,就需要指定为这个名字,在最后面会详细介绍这个name
是怎么用的。
接着需要在这个文件中添加一行代码。
|
|
意思是我们的插件名字叫hello-world-plugin
,实现这个插件功能的类名为HelloPlugin
。
在实现helloPlugin
这个类之前,我们先给这个Project添加一下依赖,因为我们最开始是通过新建文件夹的形式,然后在AS中导入这个项目,所以它还没有把groovy相关的包依赖进来。我们在项目名字上右键,选择Open Module Settings
,然后添加Dependencies,如下图所示:
最后,我们在src/main/groovy
下面新建一个一个名为com.nought.gradle.plugin
的package。
并在这个包下建立名为HelloPlugin
的类(右键new file
->输入HelloPlugin.groovy
)。
下面开始写代码,我们通过自己定义一个插件,来实现前一篇博客里面的gradle打包功能,它可以把我们指定的java代码打包成jar包,并按照配置决定是否进行混淆,并输出到一个指定的文件夹中。
3.实现Plugin接口
定义了HelloPlugin
类,我们要让它实现Plugin接口,并实现其中的apply
方法。
|
|
4.定义Extension
首先,什么是Extension?
Extension就是扩展属性,指的是你可以给你的project添加额外的gradle约定之外的其他properties属性。我们在Android项目里的build.gradle
文件中通常使用的诸如下面这种代码:
|
|
就是在给Android插件的Extension的compileSdkVersion
和buildToolsVersion
赋值。我们自己写一个插件,也要实现同样的效果。只要在gradle文件里apply了我们的自定义的插件,我们就可以给自定义的插件赋予额外的属性,并在插件里用到它们,例如你打一个jar包时可以把输出文件存放地址传入进去等等。
So,新建一个名为HelloPluginExtension
的类,表明这是HelloPlugin
的扩展属性。并在这个类里面添加一些String
类型的变量,如下所示:
|
|
很显然,其实你可输入任何Java语言中的变量类型。这些属性在apply我们的插件时,都可以在build.gradle脚本中传入。
5.在Plugin中增加自定义的task
刚才定义了Extension里面的一些属性,自定义Gradle Plugin的框架就基本是这样了,当然你要是只写一个 helloworld demo尝尝口味,那就没必要干下面的事情了。
接下来我们要在HelloPlugin
中用到这个属性,并增加一些实现了不同功能的tasks。
本例子中,我们自定义的插件可以编译Java源代码,并把生成的class文件打包成jar,再根据需求决定是不是混淆它。下面直接上代码:
|
|
代码不多,其实就是创建了4个task,彼此之间有依赖,最后再创建两个钩子任务,他们不做实际的工作,只是通过钩子任务去依赖真正实现了功能的task。大家如果看过Android Gradle Plugin的实现,就知道assembleXXX任务就是这么干的。
6.发布插件
为了让其他的项目能引用这个打包插件,需要将这个插件发布出去,我们在插件项目的根目录下的build.gradle
文件。添加下面的代码:
|
|
上面的代码就是通过groovy
插件编译打包我们的插件代码,并通过maven
插件publish到指定的服务器。我们为了调试,先将插件发布到本地的release/libs
文件夹下面就行。
7.在自己项目中应用写好的插件
现在假设我们把刚才的插件打包,发布到了release/libs
下面。这时属于本地的发布和引用,我们可以将这个libs下面的文件夹全部拷贝到自己的Android项目根目录的libs
下面去,一般可能Android项目下的根目录中没有这个文件夹,那么我们就新建一个libs
,再把gradle插件的文件夹全部丢进去。以前一篇博客的Android工程为例。
首先在项目根目录的build.gradle
文件中按照下面的方式引用:
|
|
然后在需要使用自定义插件的Module中apply这个插件。并将该自定义插件的Extension传入进去,如下所示:
|
|
这时我们的Android工程下的gradle打包脚本就干净多了,所有的任务都丢到自定义的插件里面去了。需要动态指定的属性,通过Extension就可以进行赋值,非常方便。
当需要打包时,打开Android Studio自带的终端,输入cd hellolib
进入lib工程的目录,再输入gradle packageProguardJar
或者gradle packageNoProguardJar
就可以打包了。打包出来的jar怎么给app module去引用,就不赘述了。
你甚至可以在自己的Gradle插件里再写一个copy task,直接将打好的helloLib.jar拷贝到app目录的libs下面,这样就更方便了。另外,app module下的版本号管理任务,你也可以把他们丢到自定义的插件里面去,如果你的生成环境要求你的Android工程尽可能简洁时,建议大家都封装一个自己的打包插件,deploy到公司的maven私服去。
最后贴上Gradle插件工程和引用插件工程打包的Android工程供参考。
容易遇到的问题
遇到Unsupported major.minor version 52.0
问题
这个前面我提过,在插件工程的打包脚本中,有一个交叉编译选项,如果你使用高版本的JDK编写Gradle插件,为了让你打出来的Gradle Plugin(实际上就是一个jar包),能在别人低版本的JRE上跑起来,你就必须使用这个选项。否则,人家还要去安装一个新的JDK,就很蛋疼了。
请记得加上这个:
|
|